#include "mkcallback.h"
#include <chrono>
#include <map>
#include "storagemanager.h"

//全局相机类型映射
extern std::map<std::string, std::string> g_mapCameraName;
extern std::map<std::string, int> g_mapCameraType;


/**
 * 注册或反注册MediaSource事件广播
 */
void  on_mk_media_changed(int regist,
    const mk_media_source sender) {

    string app = mk_media_source_get_app(sender);
    string stream = mk_media_source_get_stream(sender);
    string key = app + "/" + stream;
    std::string cameraName = g_mapCameraName.at(key);
    std::string info = "媒体注册";
    if(0 == regist)
    {
        info = "媒体注销";
    }

    info = cameraName + " " + info;
    char text[128] = {0};
    sprintf(text, "%s %s/%s/%s/%s",
            info.c_str(),
            mk_media_source_get_schema(sender),
            mk_media_source_get_vhost(sender),
            mk_media_source_get_app(sender),
            mk_media_source_get_stream(sender));

    log_printf(LOG_LEV, "%s", text);

//    log_printf(LOG_LEV, "%d %s/%s/%s/%s", regist,
//        mk_media_source_get_schema(sender),
//        mk_media_source_get_vhost(sender),
//        mk_media_source_get_app(sender),
//        mk_media_source_get_stream(sender));
}

/**
 * 收到rtsp/rtmp推流事件广播，通过该事件控制推流鉴权
 */
void  on_mk_media_publish(const mk_media_info url_info,
    const mk_publish_auth_invoker invoker,
    const mk_sock_info sender) {
    char ip[64];
    log_printf(LOG_LEV,
        "client info, local: %s:%d, peer: %s:%d\n"
        "%s/%s/%s/%s, url params: %s",
        mk_sock_info_local_ip(sender, ip),
        mk_sock_info_local_port(sender),
        mk_sock_info_peer_ip(sender, ip + 32),
        mk_sock_info_peer_port(sender),
        mk_media_info_get_schema(url_info),
        mk_media_info_get_vhost(url_info),
        mk_media_info_get_app(url_info),
        mk_media_info_get_stream(url_info),
        mk_media_info_get_params(url_info));

    //允许推流，并且允许转hls/mp4
    mk_publish_auth_invoker_do(invoker, NULL, 1, 1);
}

/**
 * 播放rtsp/rtmp/http-flv/hls事件广播，通过该事件控制播放鉴权
 */
void  on_mk_media_play(const mk_media_info url_info,
    const mk_auth_invoker invoker,
    const mk_sock_info sender) {

    char ip[64];
    log_printf(LOG_LEV,
        "client info, local: %s:%d, peer: %s:%d\n"
        "%s/%s/%s/%s, url params: %s",
        mk_sock_info_local_ip(sender, ip),
        mk_sock_info_local_port(sender),
        mk_sock_info_peer_ip(sender, ip + 32),
        mk_sock_info_peer_port(sender),
        mk_media_info_get_schema(url_info),
        mk_media_info_get_vhost(url_info),
        mk_media_info_get_app(url_info),
        mk_media_info_get_stream(url_info),
        mk_media_info_get_params(url_info));

    //允许播放
    mk_auth_invoker_do(invoker, NULL);
}

/**
 * 未找到流后会广播该事件，请在监听该事件后去拉流或其他方式产生流，这样就能按需拉流了
 */
int  on_mk_media_not_found(const mk_media_info url_info,
    const mk_sock_info sender) {
    char ip[64];
    log_printf(LOG_LEV,
        "client info, local: %s:%d, peer: %s:%d\n"
        "%s/%s/%s/%s, url params: %s",
        mk_sock_info_local_ip(sender, ip),
        mk_sock_info_local_port(sender),
        mk_sock_info_peer_ip(sender, ip + 32),
        mk_sock_info_peer_port(sender),
        mk_media_info_get_schema(url_info),
        mk_media_info_get_vhost(url_info),
        mk_media_info_get_app(url_info),
        mk_media_info_get_stream(url_info),
        mk_media_info_get_params(url_info));
    return 0;
}

/**
 * 某个流无人消费时触发，目的为了实现无人观看时主动断开拉流等业务逻辑
 */
void  on_mk_media_no_reader(const mk_media_source sender) {
    log_printf(LOG_LEV,
        "%s/%s/%s/%s",
        mk_media_source_get_schema(sender),
        mk_media_source_get_vhost(sender),
        mk_media_source_get_app(sender),
        mk_media_source_get_stream(sender));
}

//按照json转义规则转义webrtc answer sdp
static char *escape_string(const char *ptr) {
    char *escaped = (char*)malloc(2 * strlen(ptr));
    char *ptr_escaped = escaped;
    while (1) {
        switch (*ptr) {
        case '\r': {
            *(ptr_escaped++) = '\\';
            *(ptr_escaped++) = 'r';
            break;
        }
        case '\n': {
            *(ptr_escaped++) = '\\';
            *(ptr_escaped++) = 'n';
            break;
        }
        case '\t': {
            *(ptr_escaped++) = '\\';
            *(ptr_escaped++) = 't';
            break;
        }

        default: {
            *(ptr_escaped++) = *ptr;
            if (!*ptr) {
                return escaped;
            }
            break;
        }
        }
        ++ptr;
    }
}

static void on_mk_webrtc_get_answer_sdp_func(void *user_data, const char *answer, const char *err) {
    const char *response_header[] = { "Content-Type", "application/json", "Access-Control-Allow-Origin", "*" , NULL };
    if (answer) {
        answer = escape_string(answer);
    }
    size_t len = answer ? 2 * strlen(answer) : 1024;
    char *response_content = (char *)malloc(len);

    if (answer) {
        snprintf(response_content, len,
            "{"
            "\"sdp\":\"%s\","
            "\"type\":\"answer\","
            "\"code\":0"
            "}",
            answer);
    }
    else {
        snprintf(response_content, len,
            "{"
            "\"msg\":\"%s\","
            "\"code\":-1"
            "}",
            err);
    }

    //mk_http_response_invoker_do_string(user_data, 200, response_header, response_content);
    //mk_http_response_invoker_clone_release(user_data);
    free(response_content);
    if (answer) {
        free((void *)answer);
    }
}
/**
 * 收到http api请求广播(包括GET/POST)
 */
 //测试url : http://127.0.0.1/api/test
void  on_mk_http_request(const mk_parser parser,
    const mk_http_response_invoker invoker,
    int *consumed,
    const mk_sock_info sender) {

    char ip[64];
    log_printf(LOG_LEV,
        "client info, local: %s:%d, peer: %s:%d\n"
        "%s %s?%s %s\n"
        "User-Agent: %s\n"
        "%s",
        mk_sock_info_local_ip(sender, ip),
        mk_sock_info_local_port(sender),
        mk_sock_info_peer_ip(sender, ip + 32),
        mk_sock_info_peer_port(sender),
        mk_parser_get_method(parser),
        mk_parser_get_url(parser),
        mk_parser_get_url_params(parser),
        mk_parser_get_tail(parser),
        mk_parser_get_header(parser, "User-Agent"),
        mk_parser_get_content(parser, NULL));

    const char *url = mk_parser_get_url(parser);
    *consumed = 1;

    //拦截api: /api/test
    if (strcmp(url, "/api/test") == 0) {
        const char *response_header[] = { "Content-Type", "text/html", NULL };
        const char *content = "<html>"
            "<head>"
            "<title>hello world</title>"
            "</head>"
            "<body bgcolor=\"white\">"
            "<center><h1>hello world</h1></center><hr>"
            "<center>"
            "ZLMediaKit-4.0</center>"
            "</body>"
            "</html>";
        mk_http_body body = mk_http_body_from_string(content, 0);
        mk_http_response_invoker_do(invoker, 200, response_header, body);
        mk_http_body_release(body);
    }
    else if (strcmp(url, "/index/api/webrtc") == 0) {
        //拦截api: /index/api/webrtc
        char rtc_url[1024];
        snprintf(rtc_url, sizeof(rtc_url), "rtc://%s/%s/%s?%s", mk_parser_get_header(parser, "Host"),
            mk_parser_get_url_param(parser, "app"), mk_parser_get_url_param(parser, "stream"),
            mk_parser_get_url_params(parser));

        mk_webrtc_get_answer_sdp(mk_http_response_invoker_clone(invoker), on_mk_webrtc_get_answer_sdp_func,
            mk_parser_get_url_param(parser, "type"), mk_parser_get_content(parser, NULL), rtc_url);
    }
    else {
        *consumed = 0;
        return;
    }
}

/**
 * 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限
 */
void  on_mk_http_access(const mk_parser parser,
    const char *path,
    int is_dir,
    const mk_http_access_path_invoker invoker,
    const mk_sock_info sender) {

    char ip[64];
    log_printf(LOG_LEV,
        "client info, local: %s:%d, peer: %s:%d, path: %s ,is_dir: %d\n"
        "%s %s?%s %s\n"
        "User-Agent: %s\n"
        "%s",
        mk_sock_info_local_ip(sender, ip),
        mk_sock_info_local_port(sender),
        mk_sock_info_peer_ip(sender, ip + 32),
        mk_sock_info_peer_port(sender),
        path, (int)is_dir,
        mk_parser_get_method(parser),
        mk_parser_get_url(parser),
        mk_parser_get_url_params(parser),
        mk_parser_get_tail(parser),
        mk_parser_get_header(parser, "User-Agent"),
        mk_parser_get_content(parser, NULL));

    //有访问权限,每次访问文件都需要鉴权
    mk_http_access_path_invoker_do(invoker, NULL, NULL, 0);
}

/**
 * 在http文件服务器中,收到http访问文件或目录前的广播
 */
void  on_mk_http_before_access(const mk_parser parser,
    char *path,
    const mk_sock_info sender) {

    char ip[64];
    log_printf(LOG_LEV,
        "client info, local: %s:%d, peer: %s:%d, path: %s\n"
        "%s %s?%s %s\n"
        "User-Agent: %s\n"
        "%s",
        mk_sock_info_local_ip(sender, ip),
        mk_sock_info_local_port(sender),
        mk_sock_info_peer_ip(sender, ip + 32),
        mk_sock_info_peer_port(sender),
        path,
        mk_parser_get_method(parser),
        mk_parser_get_url(parser),
        mk_parser_get_url_params(parser),
        mk_parser_get_tail(parser),
        mk_parser_get_header(parser, "User-Agent"),
        mk_parser_get_content(parser, NULL));
    //覆盖path的值可以重定向文件
}

/**
 * 该rtsp流是否需要认证
 */
void  on_mk_rtsp_get_realm(const mk_media_info url_info,
    const mk_rtsp_get_realm_invoker invoker,
    const mk_sock_info sender) {
    char ip[64];
    log_printf(LOG_LEV,
        "client info, local: %s:%d, peer: %s:%d\n"
        "%s/%s/%s/%s, url params: %s",
        mk_sock_info_local_ip(sender, ip),
        mk_sock_info_local_port(sender),
        mk_sock_info_peer_ip(sender, ip + 32),
        mk_sock_info_peer_port(sender),
        mk_media_info_get_schema(url_info),
        mk_media_info_get_vhost(url_info),
        mk_media_info_get_app(url_info),
        mk_media_info_get_stream(url_info),
        mk_media_info_get_params(url_info));

    //rtsp播放默认鉴权
    mk_rtsp_get_realm_invoker_do(invoker, "zlmediakit");
}

/**
 * 请求认证用户密码事件，
 */
void  on_mk_rtsp_auth(const mk_media_info url_info,
    const char *realm,
    const char *user_name,
    int must_no_encrypt,
    const mk_rtsp_auth_invoker invoker,
    const mk_sock_info sender) {

    char ip[64];
    log_printf(LOG_LEV,
        "client info, local: %s:%d, peer: %s:%d\n"
        "%s/%s/%s/%s, url params: %s\n"
        "realm: %s, user_name: %s, must_no_encrypt: %d",
        mk_sock_info_local_ip(sender, ip),
        mk_sock_info_local_port(sender),
        mk_sock_info_peer_ip(sender, ip + 32),
        mk_sock_info_peer_port(sender),
        mk_media_info_get_schema(url_info),
        mk_media_info_get_vhost(url_info),
        mk_media_info_get_app(url_info),
        mk_media_info_get_stream(url_info),
        mk_media_info_get_params(url_info),
        realm, user_name, (int)must_no_encrypt);

    //rtsp播放用户名跟密码一致
    mk_rtsp_auth_invoker_do(invoker, 0, user_name);
}

/**
 * 录制mp4分片文件成功后广播
 */
void  on_mk_record_mp4(const mk_mp4_info mp4)
{
     STU_FILEINFO info;
     uint64_t start = mk_mp4_info_get_start_time(mp4);
     info.createTime = start * 1000;
     info.startTime = getStyleTime(start);
     info.duration = mk_mp4_info_get_time_len(mp4);
     info.stoptime = getStyleTime(start + info.duration);
     info.size = mk_mp4_info_get_file_size(mp4) / 1024 / 1024;
     info.path = mk_mp4_info_get_file_path(mp4);

     string app = mk_mp4_info_get_app(mp4);
     string stream = mk_mp4_info_get_stream(mp4);
     string key = app + "/" + stream;

     //确定相机属性
     info.cameraType = g_mapCameraType.at(key);
     info.cameraName = g_mapCameraName.at(key);
     log_printf(LOG_LEV, "path: %s %s %s %s %d", info.path.c_str(), info.startTime.c_str(), info.stoptime.c_str(), info.cameraName.c_str(), info.size);

     //文件新增管理
     StorageManager::getInstacne()->addOneFile(info);
}

/**
 * shell登录鉴权
 */
void  on_mk_shell_login(const char *user_name,
    const char *passwd,
    const mk_auth_invoker invoker,
    const mk_sock_info sender) {

    char ip[64];
    log_printf(LOG_LEV, "client info, local: %s:%d, peer: %s:%d\n"
        "user_name: %s, passwd: %s",
        mk_sock_info_local_ip(sender, ip),
        mk_sock_info_local_port(sender),
        mk_sock_info_peer_ip(sender, ip + 32),
        mk_sock_info_peer_port(sender),
        user_name, passwd);
    //允许登录shell
    mk_auth_invoker_do(invoker, NULL);
}

/**
 * 停止rtsp/rtmp/http-flv会话后流量汇报事件广播
 * @param url_info 播放url相关信息
 * @param total_bytes 耗费上下行总流量，单位字节数
 * @param total_seconds 本次tcp会话时长，单位秒
 * @param is_player 客户端是否为播放器
 * @param peer_ip 客户端ip
 * @param peer_port 客户端端口号
 */
void  on_mk_flow_report(const mk_media_info url_info,
    size_t total_bytes,
    size_t total_seconds,
    int is_player,
    const mk_sock_info sender) {
    char ip[64];
    log_printf(LOG_LEV, "%s/%s/%s/%s, url params: %s,"
        "total_bytes: %d, total_seconds: %d, is_player: %d, peer_ip:%s, peer_port:%d",
        mk_media_info_get_schema(url_info),
        mk_media_info_get_vhost(url_info),
        mk_media_info_get_app(url_info),
        mk_media_info_get_stream(url_info),
        mk_media_info_get_params(url_info),
        (int)total_bytes,
        (int)total_seconds,
        (int)is_player,
        mk_sock_info_peer_ip(sender, ip),
        (int)mk_sock_info_peer_port(sender));
}
